/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package interpretadorportugolvisual.interpreter;

import br.org.isvi.interpretador.portugol.OperationResolver;
import br.org.isvi.interpretador.portugol.Resolver;
import br.org.isvi.interpretador.portugol.exception.SemanticErrorException;
import br.org.isvi.interpretador.portugol.operations.AndResolver;
import br.org.isvi.interpretador.portugol.operations.AssignResolver;
import br.org.isvi.interpretador.portugol.operations.MinusResolver;
import br.org.isvi.interpretador.portugol.operations.MultResolver;
import br.org.isvi.interpretador.portugol.operations.OrResolver;
import br.org.isvi.interpretador.portugol.operations.SumResolver;
import br.org.isvi.interpretador.portugol.operations.UtilsValidator;
import br.org.isvi.interpretador.portugol.operations.XOrResolver;
import br.org.isvi.interpretador.portugol.types.BooleanSymbol;
import br.org.isvi.interpretador.portugol.types.IntegerSymbol;
import br.org.isvi.interpretador.portugol.types.Kind;
import br.org.isvi.interpretador.portugol.types.RealSymbol;
import br.org.isvi.interpretador.portugol.types.StringSymbol;
import br.org.isvi.interpretador.portugol.types.Symbol;
import br.org.isvi.interpretador.portugol.types.Variable;
import interpretadorportugolvisual.InputDialog;
import interpretadorportugolvisual.InterpretadorPortugolVisualApp;
import interpretadorportugolvisual.panels.ViewResolver;
import interpretadorportugolvisual.partes.Processador;
import interpretadorportugolvisual.relatorios.Registrador;
import java.util.HashMap;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import javax.swing.JTextPane;

/**
 *
 * @author abraaoisvioliveirasouzadocarmo
 */
public class VisualResolver implements Resolver {

    private Logger log;
    private static final String ARQUIVO_LOG = "operacoes.txt";
    private boolean logarAtivo = false;
    private JTextPane output;
    private HashMap<String, Variable> variables = new HashMap<String, Variable>();
    private Symbol[] stack = new Symbol[8];
    private int stackTop = -1;
    private ViewResolver view;

    public VisualResolver(JTextPane output, ViewResolver view) {
        log = Logger.getLogger(ARQUIVO_LOG);
        this.output = output;
        this.view = view;

        try {
            FileHandler fh;
            fh = new FileHandler(ARQUIVO_LOG, true);
            log.addHandler(fh);
            log.setLevel(Level.ALL);
            SimpleFormatter formatter = new SimpleFormatter();
            fh.setFormatter(formatter);
        } catch (Exception e) {
        }
    }

    public void logar(String msg) {
        if (logarAtivo) {
            log.info(msg);
        }
    }

    public void error(String msg) {
        if (logarAtivo) {
            log.severe(msg);
        }
    }

    public void addToStack(Symbol obj) {
        try {
            stackTop++;
            stack[stackTop] = obj;

            view.getSetaEscreveMemoria().setRecebendo();
            view.getMemoria().addToStack(obj);

            while (view.getMemoria().isStackBusy()) {
                Thread.sleep(10);
            }

            logar("adicionou para o stack:" + obj.toString());
        } catch (Exception e) {
            throw new SemanticErrorException("Estouro de pilha");
        }
    }

    public Symbol popStack() {
        try {
            int last = stackTop;
            stackTop--;

            logar("removeu do stack:" + stack[last].toString());

            view.getSetaLeMemoria().setRecebendo();
            view.getMemoria().popStack();
            while (view.getMemoria().isStackBusy()) {
                Thread.sleep(10);
            }

            ativarProcessador();

            return stack[last];
        } catch (Exception e) {
            throw new SemanticErrorException("Estouro de pilha");
        }
    }

    public void declare(Variable obj) {
        if (variables.containsKey(obj.getName())) {
            throw new SemanticErrorException("Redeclaração de Variável: " + obj.getName());
        }

        variables.put(obj.getName(), obj);

        view.getSetaEscreveMemoria().setRecebendo();
        view.getMemoria().addToData(obj);
        while (view.getMemoria().isDataBusy()) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
                error(ex.getMessage());
                ex.printStackTrace();
            }
        }
    }

    public void endOPeration(OperationResolver opr) {
        boolean ula = false;
        if (opr instanceof AssignResolver) {
            AssignResolver as = (AssignResolver) opr;

            ula = false;
        } else {
            if (opr instanceof SumResolver) {
                if ((UtilsValidator.getRealVariableValue(stack[stackTop]).getKind() == Kind.TEXTO) ||
                        (UtilsValidator.getRealVariableValue(stack[stackTop - 1]).getKind() == Kind.TEXTO)) {
                    view.getProcessador().ativar();
                }
            } else {
                ula = true;
            }
        }

        Symbol ret = opr.execute();

        if (!ula) {
            view.getProcessador().ativar();
        } else {
            view.getProcessador().ativarUla();
        }


        while (view.getProcessador().getEstado() != Processador.Estado.parado) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
                error(ex.getMessage());
                ex.printStackTrace();
            }
        }


        if (ret != null) {
            addToStack(ret);
        }

    }

    public void escreva(Symbol valor) {

        String text = output.getText();
        text = text + valor.toString();
        view.getEscreveSaida().setRecebendo();

        Registrador.registrarSaida(text);
        output.setText(text);
    }

    public void escrevaNL(Symbol valor) {
        escreva(valor);
        String text = output.getText();
        text = text + '\n';
        view.getEscreveSaida().setRecebendo();

        Registrador.registrarSaida(text);
        output.setText(text);
    }

    public Symbol getOPerationResult() {
        return popStack();
    }

    public void updateTarget(String target) {
        view.getMemoria().readCode();
        while (view.getMemoria().isCodeBusy()) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
                error(ex.getMessage());
                ex.printStackTrace();
            }
        }

        view.getBarramento().setPalavra(target);
        while (view.getBarramento().isBusy()) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
                error(ex.getMessage());
                ex.printStackTrace();
            }
        }

        ativarProcessador();
    }

    public Variable getVariable(String name) {
        if (!variables.containsKey(name)) {
            throw new SemanticErrorException("Variavel " + name + " não foi definida!");
        }

//        ativarProcessador();
        view.getMemoria().accessData(name);//acessando
        view.getSetaLeMemoria().setRecebendo();
        while (view.getMemoria().isDataBusy()) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
                error(ex.getMessage());
                ex.printStackTrace();
            }
        }
        ativarProcessador();
        return variables.get(name);
    }

    public void setLogarAtivo(boolean logarAtivo) {
        this.logarAtivo = logarAtivo;
    }

    public void performeHandlingError(String msg, int line, int col) {

        StringBuffer sbf = new StringBuffer();
        sbf.append("ERRO --> ").append(msg);

        if (line >= 0) {
            sbf.append(" na linha: ").append(line).append(" e coluna: ").append(col);
        }

        String text = output.getText();
        text = text + sbf.toString();
        text = text + '\n';

        Registrador.inserirErro(text);

        output.setText(text);
        
    }

    public int stackSize() {
        return stackTop;
    }

    public Symbol leia(Kind k) {
        Symbol ret = null;

        view.getSetaEntrada().setEsperando();
        String value = null;

        while (view.getSetaEntrada().isBusy()) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
                error(ex.getMessage());
                ex.printStackTrace();
            }
        }

        try {
            value = view.getTextoEntrada();

        } catch (Exception e) {
            throw new SemanticErrorException("Não foi possivel ler a partir do buffer de entrada!");
        }

        switch (k) {
            case TEXTO:
                ret = new StringSymbol(value);
                break;
            case INTEIRO:
                ret = IntegerSymbol.valueOf(value);
                break;
            case BOOLEANO:
                ret = BooleanSymbol.valueOf(value);
                break;
            case REAL:
                ret = RealSymbol.valueOf(value);
                break;
            default:
                break;
        }

        return ret;
    }

    /**
     * Apenas para debug.
     */
    public void printStack() {

        for (int i = 0; i <= stackTop; i++) {
            System.out.println(stack[i]);
        }
    }

    private void ativarProcessador() {
        view.getProcessador().ativar();
        while (view.getProcessador().getEstado() != Processador.Estado.parado) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
                error(ex.getMessage());
                ex.printStackTrace();
            }
        }
    }

    public Variable setVariable(Variable var, Symbol sym) {
        ativarProcessador();
        view.getSetaEscreveMemoria().setRecebendo();
        view.getMemoria().setValueData(var.getName(), sym);
        var.setValue(sym);

        while (view.getMemoria().isDataBusy()) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
                error(ex.getMessage());
                ex.printStackTrace();
            }
        }
        return var;
    }
}
